/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.usecase.helper;

import java.util.ArrayList;
import java.util.Iterator;

import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.ElementImport;
import org.eclipse.uml2.uml.Extension;
import org.eclipse.uml2.uml.Include;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.UseCase;

import com.cea.papyrus.core.helper.GraphEdgeHelper;
import com.cea.papyrus.core.helper.ModelerHelper;
import com.cea.papyrus.core.helper.NodeHelper;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.core.utils.VectorCalculus;
import com.cea.papyrus.diagraminterchange2.di2.Di2Factory;
import com.cea.papyrus.diagraminterchange2.di2.Diagram;
import com.cea.papyrus.diagraminterchange2.di2.GraphConnector;
import com.cea.papyrus.diagraminterchange2.di2.GraphEdge;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;

/**
 * Defines management of includes.
 * 
 * @author Gilles Cannenterre
 * @since 9 oct. 06
 */
public class EdgeIncludeHelper extends GraphEdgeHelper {

	/**
	 * The channel.
	 */
	private int channel = 203;

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.helper.GraphEdgeHelper#connect(org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param target 
	 * @param link 
	 * @param source 
	 * 
	 * @return 
	 */
	@Override
	public boolean connect(Element source, Element target, Element link) {
		Debug.debug(this,"connect link", channel);
		// This is the source that contains the include
		((Include)link).setName(getBaseString()+((UseCase)target).getName());
		((Include)link).setIncludingCase((UseCase)source);
		((Include)link).setAddition((UseCase)target);
		((UseCase)source).getIncludes().add(((Include)link));
		return true;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.helper.GraphEdgeHelper#deconnect(org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param target 
	 * @param link 
	 * @param source 
	 * 
	 * @return 
	 */
	@Override
	public boolean deconnect(Element source, Element target, Element link) {
		Debug.debug(this,"deconnect(Element,Element,Element)", channel);
		((Include)link).setIncludingCase(null);
		((Include)link).setAddition(null);
		return true;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.GraphEdgeFactory#canConnect(org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param target 
	 * @param source 
	 * 
	 * @return 
	 */
	@Override
	public boolean canConnect(Element source,Element target){
		if ( (source != target) && 
				((source instanceof UseCase) && (target instanceof UseCase)) ) {
			return true;
		}
		if ( (target==null) && 
				((source instanceof UseCase) ) ) {
			return true;
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.GraphEdgeFactory#canCreateGraphElement(com.cea.papyrus.diagraminterchange2.di2.GraphElement)
	 */
	/**
	 * 
	 * 
	 * @param source 
	 * 
	 * @return 
	 */
	@Override
	public boolean canCreateGraphElement(GraphElement source){
		Debug.debug(this,"canCreateGraphElement(GraphElement)", channel);
		if (source instanceof Diagram ){
			return true;
		}


		return false;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.GraphEdgeFactory#createGraphEdge(org.eclipse.uml2.uml.Element, com.cea.papyrus.diagraminterchange2.di2.Diagram)
	 */
	/**
	 * 
	 * 
	 * @param umlElement 
	 * @param diagram 
	 * 
	 * @return 
	 */
	@Override
	public ArrayList createGraphEdge(Element umlElement, Diagram diagram){
		Debug.debug(this,"createGraphEdge(Element,Diagram)", channel);
		GraphEdge ge=(GraphEdge)this.createGraphElement(umlElement, null, null, null);
		//look for UML connected UML element
		if (!(umlElement instanceof Include)){
			Debug.error(null, "EdgeIncludeFactory.createGraphEdge: "+ umlElement+ "is not an Include!", channel);
		}

		Include de=(Include)umlElement;
		Element umltarget = de.getAddition();
		Element umlsource = de.getIncludingCase();
		super.endCreateGraphElement(umlsource, umltarget, ge, diagram);
		return new ArrayList();
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.GraphEdgeFactory#createGraphElement(org.eclipse.uml2.uml.Element, com.cea.papyrus.diagraminterchange2.di2.GraphElement, org.eclipse.draw2d.geometry.Point, org.eclipse.draw2d.geometry.Dimension)
	 */
	/**
	 * 
	 * 
	 * @param umlChild 
	 * @param size 
	 * @param location 
	 * @param parent 
	 * 
	 * @return 
	 */
	@Override
	public GraphElement createGraphElement(Element umlChild, GraphElement parent, Point location, Dimension size) {
		GraphElement graphElement=super.createGraphElement(umlChild, null, location, size);
		NodeHelper nf=new NodeHelper();
		// Name of the include
		GraphElement graphNode=nf.createGraphElement(null, graphElement, new Point(0, 10), null);
		graphNode.setFontSize(9);
		graphNode.setIsVisible(false);

		// Stereotype of the include
		graphNode=nf.createGraphElement(null, graphElement, new Point(0, 30), null);
		graphNode.setFontSize(9);

		// The parent has to be connected in last, because a event will be triggered.
		if(parent!=null){
			parent.getContained().add(graphElement);
		}

		return graphElement;	
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.GraphEdgeFactory#createUMLElement(java.lang.String, org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param umlParent 
	 * @param name 
	 * 
	 * @return 
	 */
	@Override
	public Element createUMLElement(String name, Element umlParent){
		Debug.debug(this,"createUMLElement(String,Element)", channel); //$NON-NLS-1$
		return ((UseCase)umlParent).createInclude(name, null);		
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.GraphEdgeFactory#canCreateUMLElement(org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param umlParent 
	 * 
	 * @return 
	 */
	@Override
	public boolean canCreateUMLElement(Element umlParent) {
		if(umlParent instanceof UseCase) {
			return true;
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.GraphEdgeFactory#addUMLElement(org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param element 
	 * @param umlParent 
	 * 
	 * @return 
	 */
	@Override
	public boolean addUMLElement(Element element, Element umlParent) {
		if(umlParent instanceof UseCase) {
			return ((UseCase)umlParent).getIncludes().add((Include)element);
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.GraphEdgeFactory#removeUMLElement(org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param element 
	 * @param umlParent 
	 * 
	 * @return 
	 */
	@Override
	public boolean removeUMLElement(Element element, Element umlParent) {
		if(umlParent instanceof UseCase) {
			return ((UseCase)umlParent).getIncludes().remove(element);
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.GraphEdgeFactory#getBaseString()
	 */
	/**
	 * 
	 * 
	 * @return 
	 */
	@Override
	public String getBaseString() {
		return "Include_"; //$NON-NLS-1$
	}

	/**
	 * 
	 * 
	 * @param ge 
	 * @param umlsource 
	 * @param diagram 
	 * @param umlTarget 
	 */
	public void endCreateGraphElement(Element umlsource, Element umlTarget, GraphEdge ge, Diagram diagram){
		ArrayList di2suppliers=LookForElement.lookForGraphElementWODiagram(umlTarget, diagram, new ArrayList());
		ArrayList di2sources=LookForElement.lookForGraphElementWODiagram(umlsource, diagram, new ArrayList());

		GraphElement source=null;
		GraphElement target=null;


		if ((di2sources.size()!=0) || (di2suppliers.size()!=0)){
			Iterator<GraphElement> iter =di2suppliers.iterator();
			while (iter.hasNext()) {
				GraphElement element = (GraphElement) iter.next();
				if (element.getSemanticParent()==null){//to avoid the name label
					target=element;
				}

			}
			iter =di2sources.iterator();
			while (iter.hasNext()) {
				GraphElement element = (GraphElement) iter.next();
				if (element.getSemanticParent()==null){//to avoid the name label
					source=element;
				}

			}

		}

		//the source does not exists
		if((source==null) && target!=null){
			//create the source
			Element ei= LookForElement.getElementImport(((Package)umlTarget.getOwner()), umlsource);
			if(ei==null){
				ei=umlsource;
			}

			if (!(ei instanceof ElementImport)){
				source=ModelerHelper.getFactory().createGraphElement(ei, diagram,
						new Point(target.getPosition().x-3*SPACE_X,target.getPosition().y), null);}

			else  {
				//umlElement=((ElementImport)umlElement).getImportedElement();
				source=ModelerHelper.getFactory().createGraphElement((((ElementImport)ei).getImportedElement()), diagram, 
						new Point(target.getPosition().x-3*SPACE_X,target.getPosition().y), null);
			}

		}
		//the target does not exist
		else if((source!=null) && target==null){
			//create the supplier
			Element ei= LookForElement.getElementImport(((Package)umlTarget.getOwner()), umlTarget);
			if(ei==null){
				ei=umlTarget;
			}

			if (!(ei instanceof ElementImport)){
				target=ModelerHelper.getFactory().createGraphElement(ei, diagram,
						new Point(source.getPosition().x+3*SPACE_X,source.getPosition().y), null);}

			else  {
				//umlElement=((ElementImport)umlElement).getImportedElement();
				target=ModelerHelper.getFactory().createGraphElement((((ElementImport)ei).getImportedElement()), diagram, 
						new Point(source.getPosition().x+3*SPACE_X,source.getPosition().y), null);
			}
		}
		else {
//			create all
			ArrayList GraphNodes=null;
			Element ei= LookForElement.getElementImport(((Package)umlTarget.getOwner()), umlsource);
			if(ei==null){
				ei=umlsource;
			}
			if (!(ei instanceof ElementImport)){
				source=ModelerHelper.getFactory().createGraphElement(ei, diagram, new Point(30,30), null);}

			else  {
				//umlElement=((ElementImport)umlElement).getImportedElement();
				source=ModelerHelper.getFactory().createGraphElement((((ElementImport)ei).getImportedElement()), diagram, new Point(30,30), null);
			}
			ei=null;
			ei= LookForElement.getElementImport(((Package)umlTarget.getOwner()), umlTarget);
			if(ei==null){
				ei=umlTarget;
			}

			if (!(ei instanceof ElementImport)){
				target=ModelerHelper.getFactory().createGraphElement(ei, diagram, 
						new Point(source.getPosition().x+3*SPACE_X,source.getPosition().y), null);}
			else  {
				//umlElement=((ElementImport)umlElement).getImportedElement();
				target=ModelerHelper.getFactory().createGraphElement((((ElementImport)ei).getImportedElement()), diagram,
						new Point(source.getPosition().x+3*SPACE_X,source.getPosition().y), null);
			}

		}

		//all element exist!
		//1. create connector
		GraphConnector sourceConnector=Di2Factory.eINSTANCE.createGraphConnector();
		GraphConnector targetConnector=Di2Factory.eINSTANCE.createGraphConnector();

		//2. calculate position for each element

		//2.1 position of the source connector		
		Point p=LookForElement.calculateAbsolutePosition((GraphNode)source, new Point(0,0));
		sourceConnector.setPosition(new Point (p.x+((GraphNode)source).getSize().width, p.y+((GraphNode)source).getSize().height/2));
		//2.2 position if the target Connector
		p=LookForElement.calculateAbsolutePosition((GraphNode)target, new Point(0,0));
		targetConnector.setPosition(new Point(p.x, p.y+((GraphNode)target).getSize().height/2));

		//3. Connection of all element
		//3.1 connect anchor to graphEdge
		((GraphEdge)ge).getAnchor().add(SOURCE,sourceConnector);
		((GraphEdge)ge).getAnchor().add(TARGET,targetConnector);
		((GraphConnector)ge.getAnchor().get(SOURCE)).setGraphElement(source);
		((GraphConnector)ge.getAnchor().get(TARGET)).setGraphElement(target);
		if( ((Uml1SemanticModelBridge)ge.getSemanticModel()).getElement() instanceof Extension){
			VectorCalculus.setGoodPositionForExtension(ge,(GraphNode)source,(GraphNode)target);
		} else if( ((Uml1SemanticModelBridge)ge.getSemanticModel()).getElement() instanceof Association){
			VectorCalculus.setGoodPositionForAssociation(ge,(GraphNode)source,(GraphNode)target);}

		Debug.debug(this,"   target dependency link connected", channel);
		((GraphNode)source.getContainer()).getContained().add(ge);
	}
	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.helper.NodeHelper#getNewUMLElementName(org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param umlParent 
	 * 
	 * @return 
	 */
	@Override
	public String getNewUMLElementName(Element umlParent) {
		String name = ""; //$NON-NLS-1$

		// i <10000: avoid infinite loops
		for(int i=0; i< 10001; i++ ) {
			name = getBaseString()+i;
			if(null == ((UseCase)umlParent).getInclude(name, null)) {
				return name;
			}
		}
		return getBaseString()+"X"; //$NON-NLS-1$
	}
}